Release get_free_port() reservations not tied to a Server's primary port#475
Merged
esabol merged 1 commit intoJul 17, 2026
Merged
Conversation
Since gearman#472, get_free_port() leaves its reservation socket bound until release_port() is called, and Server::start() only releases its own _port. Several call sites reserve a port for something other than a Server's primary port and never released it, leaking the reservation socket for the rest of the test process: - Context (tests/context.h) and cycle_context_st (tests/cycle.cc) and round_robin.cc's Context overwrite their stored port in reset() without releasing the old one first, and never release the final port on destruction. - httpd_test.cc reserves an http_port passed only as --http-port= to a server whose own primary port is default_port(). - gearmand.cc's long_keepalive_start_TEST and config_file_SIMPLE_TEST reserve a port for --check-args validation runs that never bind it. - unittest.cc's get_free_port_TEST exercises get_free_port() directly without ever releasing what it reserves. default_port()'s single process-wide reservation is intentionally left untouched, since callers rely on it staying valid for the process's whole lifetime. Fixes gearman#474 Signed-off-by: Alexei Pastuchov <info@maximka.de>
esabol
approved these changes
Jul 17, 2026
esabol
left a comment
Member
There was a problem hiding this comment.
Looks fine. I approve these changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
#472 fixed the primary port-reservation race in
libtest::Server::start():get_free_port()now keeps its reservation socket bound untilrelease_port()is called, andServer::start()releases its own_portonce the server is confirmed up (or definitively failed).That fix only releases the
Server's own_port. Several call sites reserve a port for something other than aServerobject's primary port and never released it — the reservation socket now leaks (stays bound) for the rest of that test binary's process lifetime:tests/context.h'sContext,tests/cycle.cc'scycle_context_st, andtests/round_robin.cc'sContextall overwrite their stored port inreset()with a freshget_free_port()value without releasing the old one first, and never released the final port on destruction.tests/httpd_test.ccreserveshttp_port, passed only as--http-port=to a gearmand instance whose own primary port isdefault_port()— theServernever knows abouthttp_port.tests/gearmand.cc'slong_keepalive_start_TESTandconfig_file_SIMPLE_TESTreserve a port for--check-argsvalidation runs that never actually bind it (noServerinvolved at all).libtest/unittest.cc'sget_free_port_TESTexercisesget_free_port()directly and never releases what it reserves.Fix
Release the port explicitly wherever the above patterns discard/overwrite/finish with a reservation:
reset()-style methods now callrelease_port()on the old port before generating a new one; destructors now release the current port directly instead of wastefully fetching a new one just to abandon it (round_robin.cc's destructor previously calledreset(), which did exactly that).httpd_test.ccreleaseshttp_portright afterserver_startup()returns (success or failure) — by then gearmand has either bound it or failed outright.gearmand.cc's two--check-argstests release the port immediately after formatting it into the CLI/config argument, since--check-argsnever binds.get_free_port_TESTreleases each of its throwaway reservations at the end.default_port()'s single process-wide reservation is intentionally left untouched — callers throughout the test suite rely on it staying valid for the whole process lifetime, so releasing it would be a behavior change, not a leak fix.Test plan
t/unittest,t/cycle,t/round_robin,t/gearmand,t/httpd, plus everything else that includestests/context.h:t/drizzle,t/memcached,t/redis,t/mysql,t/tokyocabinet,t/postgres,t/ephemeral) — all compile clean.t/unittest's only failures are pre-existing environment artifacts unrelated to this change —getenv()checks that only pass undermake check's harness, and a/bin/echo-path issue specific to this NixOS dev machine).t/cycleand a heavier 16-way mix (protocol/worker/round_robin/cycle) against the pre-fix baseline on the same machine: an occasionalt/cycleflake under the heaviest combination reproduces identically with or without this change, confirming it's pre-existing high-load contention in this environment, not a regression from this PR.Fixes #474
🤖 Generated with Claude Code